// Effective C++ 条款43：学习处理模板化基类内的名称
// 核心概念
// 条款43讨论了一个在C++模板编程中常见的问题：派生类无法直接访问模板化基类中的名称。
// 这是由于C++的名称查找规则导致的，特别是在涉及依赖性名称时。

// 问题描述
template <typename Company>
class MsgSender
{
public:
    void sendClear(const std::string &message)
    {
        Company c;
        c.sendCleartext(message);
    }

    void sendSecret(const std::string &message)
    {
        Company c;
        c.sendEncrypted(message);
    }
};

template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    void sendClearMsg(const std::string &message)
    {
        // 记录消息
        sendClear(message); // 问题：这里无法编译！
    }
};

// 上面的代码无法编译，因为在LoggingMsgSender::sendClearMsg中，编译器无法找到sendClear函数。
// 为什么会发生这个问题？
// 这个问题的原因在于C++的两阶段名称查找规则：
// 1.在模板定义时，非依赖名称会立即查找
// 2.在模板实例化时，依赖名称才会查找
// 在我们的例子中，sendClear是一个依赖名称，因为它依赖于模板参数Company。
// 当编译器遇到sendClear(message)时，它不知道这是基类的成员函数，因为基类MsgSender<Company>依赖于模板参数。

// 三种解决方案
// 1. 使用this指针
template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    void sendClearMsg(const std::string &message)
    {
        // 记录消息
        this->sendClear(message); // 使用this->指明是基类成员
    }
};
// 使用this->明确指出我们要访问的是类的成员，这样编译器就知道在实例化时在基类中查找sendClear。

// 2. 使用using声明
template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    using MsgSender<Company>::sendClear; // 引入基类的sendClear到当前作用域

    void sendClearMsg(const std::string &message)
    {
        // 记录消息
        sendClear(message); // 现在可以直接使用
    }
};

// 3. 明确指定基类
template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    void sendClearMsg(const std::string &message)
    {
        // 记录消息
        MsgSender<Company>::sendClear(message); // 明确指定基类
    }
};
// 直接使用基类名称和作用域解析运算符，明确指出我们要调用的是基类的成员。

// 特殊情况：虚函数
// 需要注意的是，如果sendClear是虚函数，使用第三种方法（明确指定基类）会绕过虚函数机制，直接调用基类版本，这可能不是我们想要的。在这种情况下，前两种方法更合适。

// 更复杂的例子
// 考虑一个更复杂的情况，基类可能有特化版本：
// 一般模板
template <typename Company>
class MsgSender
{
public:
    void sendClear(const std::string &message)
    {
        Company c;
        c.sendCleartext(message);
    }
};

// 特化版本
template <>
class MsgSender<CompanyZ>
{
public:
    // 注意：这个特化版本没有sendClear方法！
    void sendSecret(const std::string &message)
    {
        CompanyZ c;
        c.sendEncrypted(message);
    }
};

template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    void sendClearMsg(const std::string &message)
    {
        this->sendClear(message); // 如果Company是CompanyZ，这里会出错！
    }
};
// 在这种情况下，如果Company是CompanyZ，this->sendClear(message)会导致编译错误，因为特化版本的MsgSender<CompanyZ>没有sendClear方法。

// 使用显式特化
// 一般模板
template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:
    void sendClearMsg(const std::string &message)
    {
        this->sendClear(message);
    }
};

// 特化版本
template <>
class LoggingMsgSender<CompanyZ> : public MsgSender<CompanyZ>
{
public:
    void sendClearMsg(const std::string &message)
    {
        // 特殊处理，不调用sendClear
    }
};
// 为LoggingMsgSender<CompanyZ>提供一个特化版本，避免调用不存在的sendClear方法。

// 最佳实践
// 1.优先使用this->：这是最简单、最直接的方法，适用于大多数情况
// 2.其次考虑using声明：当需要引入多个基类成员时更方便
// 3.避免明确指定基类：除非你确实想绕过虚函数机制
// 4.考虑特化情况：如果基类有特化版本，需要额外小心

// 总结
// 条款43的核心是：
// 1.在派生类模板中，基类模板的名称不会被自动考虑
// 2.要访问基类模板中的名称，可以使用三种方法：
//      使用this->前缀
//      使用using声明
//      使用明确的基类限定
// 3.需要特别注意基类模板有特化版本的情况
// 理解这些规则对于编写正确的模板代码至关重要，尤其是在涉及继承层次结构时。